#! /bin/sh
#
# Copyright (C) 2009-2010, 2014, 2018-2020 Free Software Foundation, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#

# Usage: convert-archive FROM TO [FROMFILE [TOFILE]]
# where FROM is dir or dirgz or dirbz2 or dirxz or cvs or git
# and   TO   is dir or dirgz or dirbz2 or dirxz or cvs or git
# This will read FROMFILE (default: archive.$FROM.tar.gz)
# and produce TOFILE (default: archive.$TO.tar.gz).

progname=$0
package=@PACKAGE@
version=@VERSION@

# func_usage
# outputs to stdout the --help usage message.
func_usage ()
{
  echo "\
Usage: convert-archive FROM TO [FROMFILE [TOFILE]]

Converts the archive of gettext infrastructure from the FROM format
to the TO format.
FROMFILE is the original file, defaulting to archive.\$FROM.tar.gz.
TOFILE is the destination file, defaulting to archive.\$TO.tar.gz.

Report bugs in the bug tracker at <https://savannah.gnu.org/projects/gettext>
or by email to <bug-gettext@gnu.org>."
}

# func_version
# outputs to stdout the --version message.
func_version ()
{
  echo "$progname (GNU $package) $version"
  echo "Copyright (C) 2009-2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law."
  echo "Written by" "Bruno Haible"
}

# func_fatal_error message
# outputs to stderr a fatal error message, and terminates the program.
func_fatal_error ()
{
  echo "convert-archive: *** $1" 1>&2
  echo "convert-archive: *** Stop." 1>&2
  exit 1
}

# Command-line option processing.

if { { test $# = 2 || test $# = 3 || test $# = 4; } \
       && case "$1" in dir | cvs | git ) true;; *) false;; esac \
       && case "$2" in dir | cvs | git ) true;; *) false;; esac; }; then
  :
else
  echo "Usage: convert-archive {dir|cvs|git} {dir|cvs|git} [fromfile [tofile]]"
  exit 1
fi

from="$1"
to="$2"
fromfile="$3"
test -n "$fromfile" || fromfile=`pwd`/archive.$from.tar.gz
tofile="$4"
test -n "$tofile" || tofile=`pwd`/archive.$to.tar.gz

unpacked=`pwd`/unpacked-files
mkdir "$unpacked" || {
  if test -d "$unpacked"; then
    func_fatal_error "directory $unpacked already exists"
  else
    func_fatal_error "cannot create directory $unpacked"
  fi
}

# Unpack the original archive.
case "$from" in
  dir*)
    { case "$from" in
        dir) cat < "$fromfile" ;;
        dirgz) gzip -d -c < "$fromfile" ;;
        dirbz2) bzip2 -d -c < "$fromfile" ;;
        dirxz) xz -d -c < "$fromfile" ;;
      esac
    } \
      | (cd "$unpacked" && tar xf -) \
      || func_fatal_error "file copy failed"
    ;;

  cvs)
    # Set variables
    # - work_dir        directory containing the temporary checkout
    work_dir=tmpwrk$$
    mkdir "$work_dir" || {
      if test -d "$work_dir"; then
        func_fatal_error "directory $work_dir already exists"
      else
        func_fatal_error "cannot create directory $work_dir"
      fi
    }

    # Set variables
    # - cvs_dir         directory containing the temporary repository
    cvs_dir=tmpcvs$$
    # Use an umask of 077, to avoid attacks that work by overwriting files in
    # the "$CVSROOT"/CVSROOT directory.
    (umask 077 && mkdir "$cvs_dir") || {
      if test -d "$cvs_dir"; then
        func_fatal_error "directory $cvs_dir already exists"
      else
        func_fatal_error "cannot create directory $cvs_dir"
      fi
    }
    CVSROOT=`pwd`/"$cvs_dir"
    unset CVS_CLIENT_LOG
    unset CVS_CLIENT_PORT
    unset CVS_IGNORE_REMOTE_ROOT
    unset CVS_LOCAL_BRANCH_NUM
    unset CVS_NOBASES
    unset CVS_PASSFILE
    unset CVS_PASSWORD
    unset CVS_PROXY_PORT
    unset CVS_RCMD_PORT
    unset CVS_RSH
    unset CVS_SERVER
    unset CVS_SERVER_SLEEP
    CVS_SIGN_COMMITS=
    export CVS_SIGN_COMMITS
    unset CVS_SSH
    unset CVS_VERIFY_CHECKOUTS
    unset CVS_VERIFY_TEMPLATE
    unset CVSIGNORE
    unset CVSREAD
    unset CVSREADONLYFS
    unset CVSUMASK
    unset CVSWRAPPERS

    # Need to pass -d "$CVSROOT", because there may be a CVS directory in the
    # current directory.
    cvs -d "$CVSROOT" init || func_fatal_error "cvs init failed"
    gzip -d -c < "$fromfile" | (cd "$cvs_dir" && tar xf -)

    # A witness that contains all versions.
    # Can be e.g. ABOUT-NLS, intl/ChangeLog, m4/gettext.m4.
    witness='ABOUT-NLS'

    # Get list of tags or versions.
    sed_extract_tags_from_log1='/^symbolic names:/{
:a
/^	/p
n
ba
}
/^keyword substitution:/q'
    sed_extract_tags_from_log2='s/^	\([^:]*\):.*/\1/'
    rlog_harmless_warning_regex="warning: Unknown phrases like \`commitid \.\.\.;' are present\."
    tags=`
      ( LC_ALL=C rlog "$cvs_dir"/archive/$witness,v 2>&1 1>&3 \
        | grep -v "$rlog_harmless_warning_regex" 1>&2 ) 3>&1 \
      | sed -n -e "$sed_extract_tags_from_log1" \
      | sed -e "$sed_extract_tags_from_log2"`
    test -n "$tags" || func_fatal_error "no release tags found"
    sed_tag_to_version='s/_/./g'
    for tag in $tags; do
      if test $tag != release; then
        version=`echo "$tag" | sed -e "$sed_tag_to_version"`
        (cd "$work_dir"
         cvs -d "$CVSROOT" checkout -r"$tag" archive > /dev/null 2> cvs.err \
           || { cat cvs.err 1>&2; exit 1; }
         cat cvs.err | grep -v '^cvs checkout: Updating'
         find archive -name CVS -type d -print | xargs rm -rf
         rm -rf CVS
        ) || func_fatal_error "cvs checkout failed"
        mv "$work_dir"/archive "$unpacked/$version"
      fi
    done
    rm -rf "$cvs_dir"
    rm -rf "$work_dir"
    ;;

  git)
    # Set variables
    # - work_dir        directory containing the temporary checkout
    work_dir=tmpwrk$$
    mkdir "$work_dir" || {
      if test -d "$work_dir"; then
        func_fatal_error "directory $work_dir already exists"
      else
        func_fatal_error "cannot create directory $work_dir"
      fi
    }

    mkdir "$work_dir/master" || func_fatal_error "mkdir failed"
    gzip -d -c < "$fromfile" | (cd "$work_dir/master" && tar xf -)
    (unset GIT_CONFIG
     unset XDG_CONFIG_HOME
     unset HOME
     GIT_CONFIG_NOSYSTEM=1; export GIT_CONFIG_NOSYSTEM
     cd "$work_dir"
     tags=`cd master && git tag`
     test -n "$tags" || func_fatal_error "no release tags found"
     for tag in $tags; do
       if test $tag != empty; then
         version=$tag
         (cd master && git checkout -q $tag) \
           || func_fatal_error "git checkout failed"
         rm -f master/.gitignore
         mv master/.git .git
         mkdir "$unpacked/$version" || func_fatal_error "mkdir failed"
         (cd master && tar cf - .) | (cd "$unpacked/$version" && tar xf -) \
           || func_fatal_error "file copy failed"
         mv .git master/.git
       fi
     done
    )
    rm -rf "$work_dir"
    ;;
esac

# Find a good 'tar' program.
existing_file=gettext-0.10.35/ABOUT-NLS
TAR=tar
TAR_OPTIONS=
for prog in tar gnutar gtar; do
  if (cd "$unpacked" && $prog cf - --owner=root --group=root "$existing_file") >/dev/null 2>&1; then
    TAR=$prog
    TAR_OPTIONS='--owner=root --group=root'
    break
  fi
done

# Create the target archive.
case "$to" in
  dir*)
    (cd "$unpacked" && $TAR cf - $TAR_OPTIONS *) \
      | { case "$to" in
            dir) cat > "$tofile" ;;
            dirgz) gzip -c -9 > "$tofile" ;;
            dirbz2) bzip2 -c -9 > "$tofile" ;;
            dirxz) xz -c -5 > "$tofile" ;;
          esac
        } \
      || func_fatal_error "archive creation failed"
    ;;

  cvs)
    # Set variables
    # - cvs_dir         directory containing the temporary repository
    cvs_dir=autopoint-files
    # Use an umask of 077, to avoid attacks that work by overwriting files in
    # the "$CVSROOT"/CVSROOT directory.
    (umask 077 && mkdir "$cvs_dir") || {
      if test -d "$cvs_dir"; then
        func_fatal_error "directory $cvs_dir already exists"
      else
        func_fatal_error "cannot create directory $cvs_dir"
      fi
    }
    CVSROOT=`pwd`/"$cvs_dir"
    unset CVS_CLIENT_LOG
    unset CVS_CLIENT_PORT
    unset CVS_IGNORE_REMOTE_ROOT
    unset CVS_PASSFILE
    unset CVS_PASSWORD
    unset CVS_RCMD_PORT
    unset CVS_RSH
    unset CVS_SERVER
    unset CVS_SERVER_SLEEP
    unset CVSIGNORE
    unset CVSREAD
    unset CVSUMASK
    unset CVSWRAPPERS

    # Set a nonstandard variable, for a good-looking cvs history.
    cvsuser=bruno
    gcc -shared -fPIC -O cvsuser.c -o cvsuser.so
    cvsuser_hack=`pwd`/cvsuser.so

    # Need to pass -d "$CVSROOT", because there may be a CVS directory in the
    # current directory.
    cvs -d "$CVSROOT" init || func_fatal_error "cvs init failed"

    # Add the contents of the unpacked directory to the repository.
    (cd "$unpacked"
     for version in *; do
       cvsver=`echo "$version" | sed -e 's/\./_/g'`
       (cd $version
        CVSUSER=$cvsuser LD_PRELOAD=$cvsuser_hack \
        cvs -d "$CVSROOT" import -m "Import $version" archive release "$cvsver" > /dev/null
       ) || func_fatal_error "cvs import failed"
       # In order to avoid keyword substitution, we have to use "cvs admin"
       # in a temporary checkout.
       mkdir tmpcheckout || func_fatal_error "mkdir failed"
       (cd tmpcheckout \
        && cvs -d "$CVSROOT" checkout -r"$cvsver" archive > /dev/null \
        && cvs -d "$CVSROOT" admin -ko `find . -type f -print | sed -e 's,^\./,,' | grep -v '^CVS/' | grep -v '/CVS/'` > /dev/null
       ) 2> cvs.err || { cat cvs.err 1>&2; func_fatal_error "cvs checkout or admin failed"; }
       rm -rf tmpcheckout
     done
    )
    (cd "$cvs_dir" && $TAR cf - $TAR_OPTIONS archive) \
      | gzip -c -9 > "$tofile" \
      || func_fatal_error "archive creation failed"
    rm -rf "$cvs_dir" cvsuser.so
    ;;

  git)
    git_dir=`pwd`/tmpgit$$
    mkdir "$git_dir" || func_fatal_error "mkdir failed"
    unset GIT_CONFIG
    unset XDG_CONFIG_HOME
    unset HOME
    GIT_CONFIG_NOSYSTEM=1; export GIT_CONFIG_NOSYSTEM
    (cd "$git_dir" && {
      git init -q
      git config user.name 'GNU Gettext Build'
      git config user.email 'bug-gnu-gettext@gnu.org'
      touch .gitignore
      git add .
      git commit --author="Bruno Haible <bruno@clisp.org>" \
                 --message="Empty directory" 2>&1 >/dev/null
      git tag empty
    }) || func_fatal_error "git init failed"
    sed_remove_leading_dot='s,^\./,,'
    sed_remove_git_infrastructure='/^\.git/d'
    (cd "$unpacked"
     for version in *; do
       (cd $version && tar cf - .) | \
       (cd "$git_dir" && {
         prev_files=`find . -type f | sed -e "$sed_remove_leading_dot" -e "$sed_remove_git_infrastructure"`
         if test -n "$prev_files"; then
           git rm -q $prev_files
         fi
         tar xf -
         git add .
         git commit --author="Bruno Haible <bruno@clisp.org>" \
                    --message="Import $version" 2>&1 >/dev/null
         git tag "$version"
       }) || func_fatal_error "file copy into git repository failed"
     done
    )
    (cd "$git_dir" && git reset -q --hard empty && git repack -a -d -q) \
      || func_fatal_error "git reset or repack failed"
    (cd "$git_dir" && $TAR cf - $TAR_OPTIONS .git) \
      | gzip -c -9 > "$tofile" \
      || func_fatal_error "archive creation failed"
    rm -rf "$git_dir"
    ;;
esac

rm -rf "$unpacked"
